#ifndef _GC_COMMON_LIST_H_
#define _GC_COMMON_LIST_H_

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TYPE_INT 1
#define TYPE_DOUBLE 2
#define TYPE_STRING 3
#define TYPE_ARRAY 4

typedef union _gc_list_val_t {
	long long int_val;
	double dbl_val;
	char * str_val;
} gc_list_data_val_t;

typedef struct _gc_list_data_t {
	int ver;
	int type;
	char * key;
	gc_list_data_val_t data;
	struct _gc_list_data_t * next;
	struct _gc_list_data_t * pre;
} gc_list_data_t;

typedef struct _gc_list_t {
	size_t size;
	gc_list_data_t * head;
	gc_list_data_t * tail;
} gc_list_t;

gc_list_t * 
gc_list_create() {
	gc_list_t * list = malloc(sizeof(gc_list_t));
	memset(list, 0, sizeof(gc_list_t));
	return list;
}

void 
gc_list_free(gc_list_t *list) {
	if(list != NULL) {
		gc_list_data_t * cur = list->head, *next;
		while(cur != NULL) {
			next = cur->next;
			if(cur->key != NULL) free(cur->key);
			if((cur->type == TYPE_STRING || cur->type == TYPE_ARRAY) 
					&& cur->data.str_val != NULL) {
				free(cur->data.str_val);
			}
			free(cur);
			cur = next;
		}
		free(list);
		list = NULL;
	}
}

inline static gc_list_data_t * 
gc_list_data_create(const char * key, size_t key_len,
			const char * val, size_t val_len, int val_type, int version) {
	gc_list_data_t *gd = malloc(sizeof(gc_list_data_t));
	gd->ver = version;
	gd->type = val_type;
	gd->key = strndup(key, key_len);
	switch(val_type) {
		case TYPE_INT:
			gd->data.int_val = atoll(val);
			break;
		case TYPE_DOUBLE:
			gd->data.dbl_val = atof(val);
			break;
		case TYPE_STRING:
		case TYPE_ARRAY:
		default:
			gd->data.str_val = strndup(val, val_len);
	}
	return gd;
}

gc_list_data_t * 
gc_list_push(gc_list_t *list, const char *key, size_t key_len,
		const char *val, size_t val_len, int val_type, int version) {
	gc_list_data_t * gd = gc_list_data_create(key, key_len, val, val_len, val_type, version);
	gd->next = NULL;
	gd->pre = list->tail;

	if(list->size == 0) {
		list->head = gd;
	}

	if(list->tail != NULL) {
		list->tail->next = gd;
	}

	list->tail = gd;

	++ list->size;
	return gd;
}

void 
gc_list_print(gc_list_t * list) {
	if(list == NULL) {
		printf("List is empty!\n");
	} else {
		printf("List:\n");	

		gc_list_data_t * head = list->head;
		while(head != NULL) {
			printf(" == %s(%d) = %s\n", head->key, head->ver, head->data.str_val);
			head = head->next;
		}
	}
}

#endif
